home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Ian & Stuart's Australian Mac 1993 September
/
clonecd
/
September 93.img
/
Archives
/
Fun, Tricks & Hacks
/
Silent Alarm, not!
/
SonicAlarm.c
< prev
next >
Wrap
C/C++ Source or Header
|
1992-06-18
|
46KB
|
1,618 lines
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// This application will use Sound Input and Output to hopefully act
// like a car alarm. It samples the surrounding volume and it a sound
// that is considered too loud is found while the alarm is on, it will
// play an alarm sound.
//
// Since I started using so many System 7 features, and I know that
// it's not likely they'll ever make their way into an older system, I
// decided to only run on a System 7 or later machine. Sorry if you
// hate System 7, but too bad for you cause you're missing the future.
// Another feature that's required is level metering, which is
// standard for the Apple built-in sound input device. It's also
// works much better in color.
//
// Something else you'll notice is that I have a state machine to run
// this application. I know it's not as clean of code as I usually
// would release, but this is the best version I had ready for MacHack
// '92. I also hate the Dialog Manager and using the windows that I
// have here would have been a royal pain (in my expert opinion) if I
// had tried using dialogs. Instead I used standard windows and drive
// everything from the standard event loop. I even implement modal
// dialogs this way.
//
// This if the first version I've ever released, and by the time
// you're reading this I've probably found bugs and fixed them.
//
// Jim Reekes, Polterzeitgeist
//
// Apple Computer, Inc.
// 20525 Mariani Ave. MS: 81-KS
// Cupertino, CA 95014
//
// AppleLink: REEKES
// include the Think headers or the MPW ones
// I always change my Think headers from the standard one shipped with Think.
//
#ifdef THINK_C
#include <MacHeaders>
#else
#pragma load "MPWHeaders"
#endif
#include <Palettes.h>
#ifndef __SONICALARM__
#include "SonicAlarm.h"
#endif
#ifndef __SONICALARMMISC__
#include "SonicAlarmMisc.h"
#endif
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// The "g" prefix is used to emphasize that a variable is global.
// state of the alarm (on, off, pending, etc.)
short gAlarmState;
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// prototypes
void main(void);
void AssertFeatures(void);
void Initialize(void);
void Terminate(void);
void EventLoop(void);
void DoEvent(EventRecord *event);
void DoUpdate(WindowPtr window);
void DoActivate(WindowPtr window, Boolean becomingActive);
void DoContentClick(WindowPtr window, EventRecord *event);
void DoIdle(void);
void AdjustMenus(Boolean updateMBar);
void DoMenuCommand(long menuResult);
void DoKeyDown(WindowPtr window, unsigned char key, short modifiers);
void CloseMyWindow(void *window);
void CreateAlarmWindow(void);
void DrawAlarmWindow(AlarmWindowPtr alarm);
void UpdateAlarmActivated(AlarmWindowPtr alarm);
void AlarmWindowClick(WindowPtr window, EventRecord *event);
void AlarmWindowKeyDown(AlarmWindowPtr alarm, unsigned char key, short modifiers);
void ConfigureAlarm(void);
void DrawConfigureWindow(ConfigurationWindowPtr window);
void DrawSensitivityCntl(ConfigurationWindowPtr configure);
void TrackSensitivityCntl(ConfigurationWindowPtr configure, Point mouse);
void UpdateTestLight(ConfigurationWindowPtr configure, Boolean on);
void UpdateLevels(ConfigurationWindowPtr window);
void ConfiguringWindowClick(ConfigurationWindowPtr configure, EventRecord *event);
void ConfigurationKeyDown(ConfigurationWindowPtr configure, unsigned char key, short modifiers);
void SetPassword(void);
void DrawSetPassword(SetPasswordWindowPtr setPassword);
void DoPasswordOKButton(SetPasswordWindowPtr setPassword);
void SetPasswordClick(SetPasswordWindowPtr setPassword, EventRecord *event);
void SetPasswordKeyDown(SetPasswordWindowPtr setPassword, unsigned char key, short modifiers);
void ActivateAlarm(void);
void DeactivateAlarm(void);
void GetPassword(void);
Boolean IsPasswordValid(DeactivationWindowPtr deactivation);
void DeactivationOKButton(DeactivationWindowPtr deactivation);
void DeactivationWindowClick(DeactivationWindowPtr deactivation, EventRecord *event);
void DrawActivationWindow(ActivationWindowPtr activation);
void ActivatingWindowClick(WindowPtr window, EventRecord *event);
void ActivationWindowKeyDown(ActivationWindowPtr activation, unsigned char key, short modifiers);
void DrawDeactivationWindow(DeactivationWindowPtr deactivation);
void DeactivationKeyDown(DeactivationWindowPtr deactivation, unsigned char key, short modifiers);
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void main(void)
{
MaxApplZone(); // expand the heap so code segments load at the top
Initialize(); // initialize the program
EventLoop(); // call the main event loop
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// check for any and all of the necessary features I need to use, such as _WaitNextEvent
// at this point nothing can be relied upon, the toolbox may not have been inited yet
void AssertFeatures(void)
{
long response;
OSErr err;
// we'll be using DeviceLoop and GetGray, so it better be System 7.0
err = Gestalt(gestaltSystemVersion, &response);
if ( (err != noErr) || (response < kSystemSeven) )
AlertUser(noErr, eFeatureNotAvailable, kFatalError);
if ( !HasSoundInput() )
AlertUser(noErr, eNoSndInDevice, kFatalError);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// initialize all of the globals, the toolbox, and other things my app needs
void Initialize()
{
Handle menuBar;
EventRecord event;
short count;
OSErr err;
gAlarmState = kDeactivatedState;
SetSensitivity(kDefaultSensitivity);
InitGraf(&qd.thePort);
InitFonts();
InitWindows();
InitMenus();
TEInit();
InitDialogs(nil);
InitCursor();
// pull the application to the front layer
for (count = 1; count <= 3; count++)
EventAvail(everyEvent, &event);
AssertFeatures();
menuBar = GetNewMBar(rMenuBar);
if (menuBar == nil)
AlertUser(MemError(), eNoMemory, kFatalError);
SetMenuBar(menuBar);
DisposHandle(menuBar);
AddResMenu(GetMHandle(mApple), 'DRVR');
DrawMenuBar();
err = InitSndTools();
if (err != noErr)
AlertUser(err, eFatalErr, kFatalError);
err = CreateLevelArray();
if (err != noErr)
AlertUser(err, eFatalErr, kFatalError);
// test for minimal memory requirements after allocating application's stuff
if (FailLowMemory(0))
AlertUser(noErr, eNoMemory, kFatalError);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// do any clean up before we die
void Terminate()
{
WindowPtr aWindow;
do {
aWindow = FrontWindow(); // get the current front window
if (aWindow != nil)
CloseMyWindow(aWindow); // close this window
} while (aWindow != nil);
CloseSndTools();
ExitToShell(); // exit if no cancellation
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// loop forever checking for events to handle, WaitNextEvent will return false if
// there is no events (nullEvents are not returned)
void EventLoop(void)
{
EventRecord event;
do {
if (WaitNextEvent(everyEvent, &event, GetSleep(), nil))
DoEvent(&event);
else
DoIdle();
} while (true);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void DoEvent(EventRecord *event)
{
WindowPtr window;
long menuResult;
short part;
short err;
unsigned char key;
switch (event->what) {
case mouseDown:
part = FindWindow(event->where, &window);
// for modal windows we only handle clicks in the window and in the menu
if ( IsWindowModal(FrontWindow())
&& (part != inMenuBar)
&& (part != inContent) )
{
SysBeep(3);
break;
}
switch (part) {
case inMenuBar:
AdjustMenus(false);
menuResult = MenuSelect(event->where);
if (HighWord(menuResult) != 0)
DoMenuCommand(menuResult);
HiliteMenu(0);
break;
case inSysWindow:
SystemClick(event, window);
break;
case inContent:
if (window != FrontWindow()) {
if (IsWindowModal(FrontWindow()))
SysBeep(3);
else
SelectWindow(window);
}
else
DoContentClick(window, event);
break;
case inDrag:
DragWindow(window, event->where, &qd.screenBits.bounds);
break;
}
break;
case keyDown:
case autoKey:
// handle a command key for menus, or give it to the front window
key = event->message & charCodeMask;
if (event->modifiers & cmdKey) {
AdjustMenus(false);
menuResult = MenuKey(key);
if (HighWord(menuResult) != 0) {
if (event->what != autoKey) {
DoMenuCommand(menuResult);
HiliteMenu(0);
}
}
else
DoKeyDown(FrontWindow(), key, event->modifiers);
}
else
DoKeyDown(FrontWindow(), key, event->modifiers);
break;
case updateEvt:
DoUpdate((WindowPtr) event->message);
break;
case diskEvt:
if (HighWord(event->message) != noErr) {
err = DIBadMount(*(PointPtr)kDITopLeft, event->message);
}
break;
case activateEvt:
DoActivate((WindowPtr)event->message, event->modifiers & activeFlag);
break;
case osEvt:
switch (event->message >> 24) {
case suspendResumeMessage:
// suspend/resume is also an activate/deactivate
DoActivate(FrontWindow(), event->message & resumeFlag);
break;
}
break;
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void DoUpdate(WindowPtr window)
{
SetPort(window);
BeginUpdate(window); // this sets up the visRgn
if (!EmptyRgn(window->visRgn)) { // draw if updating needs to be done
switch (GetMyWindowType(window)) {
case kAlarmWindow:
DrawAlarmWindow((AlarmWindowPtr)window);
break;
case kActivationWindow:
DrawActivationWindow((ActivationWindowPtr)window);
break;
case kConfigurationWindow:
DrawConfigureWindow((ConfigurationWindowPtr)window);
break;
case kDeactivationWindow:
DrawDeactivationWindow((DeactivationWindowPtr)window);
break;
case kSetPasswordWindow:
DrawSetPassword((SetPasswordWindowPtr)window);
break;
}
}
EndUpdate(window);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void DoActivate(WindowPtr window, Boolean becomingActive)
{
if (window == nil)
return;
SetPort(window);
ActivateControls(window, becomingActive);
// do any additional activation shit, like outlines for default buttons
switch (GetMyWindowType(window)) {
case kConfigurationWindow:
DeviceLoop(window->visRgn, (DeviceLoopDrawingProcPtr)OutlineControl,
(long)(FindMyControl(window, kConfigurationOK)), 0);
break;
case kDeactivationWindow:
DeviceLoop(window->visRgn, (DeviceLoopDrawingProcPtr)OutlineControl,
(long)(FindMyControl(window, kDeactivationOK)), 0);
break;
case kSetPasswordWindow:
DeviceLoop(window->visRgn, (DeviceLoopDrawingProcPtr)OutlineControl,
(long)(FindMyControl(window, kSetPasswordOK)), 0);
break;
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void DoContentClick(WindowPtr window, EventRecord *event)
{
SetPort(window);
switch (GetMyWindowType(window)) {
case kAlarmWindow:
AlarmWindowClick(window, event);
break;
case kActivationWindow:
ActivatingWindowClick(window, event);
break;
case kConfigurationWindow:
ConfiguringWindowClick((ConfigurationWindowPtr)window, event);
break;
case kDeactivationWindow:
DeactivationWindowClick((DeactivationWindowPtr)window, event);
break;
case kSetPasswordWindow:
SetPasswordClick((SetPasswordWindowPtr)window, event);
break;
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// do all of the idle time handling base on the application's current state
// I've tried to centralize the changing of the global state within this routine
// to avoid bugs that could be cause by other routines which might change a global
void DoIdle(void)
{
AlarmWindowPtr alarm;
ActivationWindowPtr activation;
DeactivationWindowPtr deactivation;
ConfigurationWindowPtr configuration;
OSErr err;
// update the averaging level of sound volume
if (gAlarmState > kDeactivatedState)
UpdateAverageLevel();
switch (gAlarmState) {
case kActivatingState:
// update the count down numbers, and when it's expired activate the alarm
activation = FindMyWindow(kActivationWindow);
FailIf(activation == nil, Failure);
SetPort((WindowPtr)activation);
DrawCountDown((WindowPtr)activation, activation->delayPt,
&activation->lastTickCount, &activation->secondsCountDown);
if (activation->secondsCountDown < 0) {
CloseMyWindow(activation);
CreateAlarmWindow();
gAlarmState = kActivatedState;
err = PlaySound(kAlarmActivatedSnd);
FailIf(err != noErr, Failure);
}
break;
case kDeactivatingState:
// update the count down numbers, and if it's expired activate the alarm
// the intention is that the user should have properly turned off the
// alarm by selecting the OK button in the window
deactivation = FindMyWindow(kDeactivationWindow);
FailIf(deactivation == nil, Failure);
SetPort((WindowPtr)deactivation);
DrawCountDown((WindowPtr)deactivation, deactivation->delayPt,
&deactivation->lastTickCount, &deactivation->secondsCountDown);
if (deactivation->secondsCountDown < 0) {
CloseMyWindow(deactivation);
err = PlaySound(kAlarmTriggeredSnd);
FailIf(err != noErr, Failure);
}
break;
case kConfiguringState:
// this window is used to set the alarm's configuration, so update the
// levels indicator which shows the user the current volume level
configuration = FindMyWindow(kConfigurationWindow);
FailIf(configuration == nil, Failure);
SetPort((WindowPtr)configuration);
UpdateLevels(configuration);
UpdateTestLight(configuration, IsVolumeTooLoud());
break;
case kActivatedState:
// the alarm is on, so check for a loud sound which will turn on the alarm
if (IsVolumeTooLoud()) {
err = PlaySound(kAlarmTriggeredSnd);
FailIf(err != noErr, Failure);
}
// continue to flash the alarm message, telling intruders the alarm is on
alarm = FindMyWindow(kAlarmWindow);
FailIf(alarm == nil, Failure);
SetPort((WindowPtr)alarm);
UpdateAlarmActivated(alarm);
break;
}
Failure:
return;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// To disable or enable an entire menu, pass 0 as the item
// We need to call DrawMenuBar when ever changing the state of an entire menu
// and this would happen if the user hadn't used a menu command, which calls
// HiliteMenu and that screws up the appearance of the menu. This appears to be
// a bug in the Menu Manager, because is doesn't notice that the menu it's
// unhiliting has been disabled and redrawn. The hilite logic simple inverses
// the menu title area, and this then makes it look enabled. If I always call
// HiliteMenu here it tricks the Menu Manager to do the right thing.
void AdjustMenus(Boolean updateMBar)
{
Boolean isModal;
MenuHandle menu;
HiliteMenu(0);
isModal = IsWindowModal(FrontWindow()) || (gAlarmState == kActivatedState);
menu = GetMHandle(mApple);
if (isModal)
DisableItem(menu, 0);
else
EnableItem(menu, 0);
menu = GetMHandle(mFile);
if (isModal)
DisableItem(menu, 0);
else
EnableItem(menu, 0);
menu = GetMHandle(mEdit);
if (isModal)
DisableItem(menu, 0);
else
EnableItem(menu, 0);
menu = GetMHandle(mAlarm);
if (isModal)
DisableItem(menu, 0);
else
EnableItem(menu, 0);
if (updateMBar)
DrawMenuBar();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void DoMenuCommand(long menuResult)
{
Str255 daName;
short menuItem;
menuItem = LowWord(menuResult);
switch (HighWord(menuResult)) {
case mApple:
switch (menuItem) {
case iAbout:
DoActivate(FrontWindow(), false);
Alert(rAboutAlert, nil);
break;
default:
GetItem(GetMHandle(mApple), menuItem, daName);
OpenDeskAcc(daName);
break;
}
break;
case mFile:
switch (menuItem) {
case iQuit:
Terminate();
break;
}
break;
case mAlarm:
switch (menuItem) {
case iSetPassword:
SetPassword();
break;
case iActivate:
ConfigureAlarm();
break;
}
break;
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// dispatch to the window's handler passing it the key and modifiers
void DoKeyDown(WindowPtr window, unsigned char key, short modifiers)
{
switch (GetMyWindowType(window)) {
case kAlarmWindow:
AlarmWindowKeyDown((AlarmWindowPtr)window, key, modifiers);
break;
case kActivationWindow:
ActivationWindowKeyDown((ActivationWindowPtr)window, key, modifiers);
break;
case kDeactivationWindow:
DeactivationKeyDown((DeactivationWindowPtr)window, key, modifiers);
break;
case kConfigurationWindow:
ConfigurationKeyDown((ConfigurationWindowPtr)window, key, modifiers);
break;
case kSetPasswordWindow:
SetPasswordKeyDown((SetPasswordWindowPtr)window, key, modifiers);
break;
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Close a window. This handles desk accessory and any application windows.
void CloseMyWindow(void *window)
{
Boolean isModal;
if (IsDAWindow(window))
CloseDeskAcc(((WindowPeek)window)->windowKind);
else {
switch (GetWRefCon(window)) {
case kAlarmWindow:
if (((AlarmWindowPtr)window)->message != nil)
ReleaseResource((Handle)((AlarmWindowPtr)window)->message);
break;
case kActivationWindow:
if (((ActivationWindowPtr)window)->message != nil)
ReleaseResource((Handle)((ActivationWindowPtr)window)->message);
break;
case kConfigurationWindow:
if (((ConfigurationWindowPtr)window)->message != nil)
ReleaseResource((Handle)((ConfigurationWindowPtr)window)->message);
if (((ConfigurationWindowPtr)window)->sensitivityTitle != nil)
ReleaseResource((Handle)((ConfigurationWindowPtr)window)->sensitivityTitle);
if (((ConfigurationWindowPtr)window)->max != nil)
ReleaseResource((Handle)((ConfigurationWindowPtr)window)->max);
if (((ConfigurationWindowPtr)window)->min != nil)
ReleaseResource((Handle)((ConfigurationWindowPtr)window)->min);
break;
case kDeactivationWindow:
if (((DeactivationWindowPtr)window)->message != nil)
ReleaseResource((Handle)((DeactivationWindowPtr)window)->message);
break;
case kSetPasswordWindow:
if (((SetPasswordWindowPtr)window)->message != nil)
ReleaseResource((Handle)((SetPasswordWindowPtr)window)->message);
break;
}
isModal = IsWindowModal(window);
CloseWindow(window);
if (isModal)
AdjustMenus(true);
DisposePtr(window);
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// this window is all black and will cover all screens, the message area is drawn
// onto the main screen's area
void CreateAlarmWindow(void)
{
AlarmWindowPtr alarm;
Rect bigRect;
alarm = GetMyWindow(sizeof(AlarmWindow), kAlarmWindow);
FailIf(alarm == nil, NoWindow)
alarm->message = GetString(kAlarmMessage);
FailIf(alarm->message == nil, NoString)
// nasty way to get a rect that covers all screens
bigRect = (**(*(RgnHandle *)GrayRgn)).rgnBBox;
SizeWindow((WindowPtr)&alarm->window, bigRect.right - bigRect.left,
bigRect.bottom - bigRect.top, false);
MoveWindow((WindowPtr)&alarm->window, bigRect.left, bigRect.top, true);
// get the main area for drawing the screen, below the menubar
alarm->msgRect = qd.screenBits.bounds;
GlobalToLocal(&TopLeft(alarm->msgRect));
GlobalToLocal(&BottomRight(alarm->msgRect));
alarm->msgRect.top += GetMBarHeight() + kSpaceBelowMBar;
SetPort((WindowPtr)alarm);
ForeColor(whiteColor);
BackColor(blackColor);
ShowWindow((WindowPtr)alarm);
EraseRect(&((WindowPtr)alarm)->portRect); // erase it all black quickly
return;
NoString:
CloseMyWindow(alarm);
NoWindow:
AlertUser(MemError(), eNoWindow, kFatalError);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// need to correctly update the main screen based on the current alarm state
// which is just all black and since the back color is set to black we only have
// to call erase rect
void DrawAlarmWindow(AlarmWindowPtr alarm)
{
EraseRect(&((WindowPtr)alarm)->portRect);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// the alarm is on, so draw the warning message
void UpdateAlarmActivated(AlarmWindowPtr alarm)
{
long curTicks;
curTicks = TickCount();
if (alarm->lastTickCount + kOneSecondTicks <= curTicks) {
alarm->lastTickCount = curTicks;
SetPort((WindowPtr)&alarm->window);
if (alarm->drawWhite)
ForeColor(whiteColor);
else
ForeColor(redColor);
alarm->drawWhite = !alarm->drawWhite;
TextFont(kAlarmFontID);
TextSize(kAlarmFontSize);
HLock((Handle)alarm->message);
TextBox(*alarm->message + 1, **alarm->message, &alarm->msgRect, teJustCenter);
HUnlock((Handle)alarm->message);
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void AlarmWindowClick(WindowPtr window, EventRecord *event)
{
Point mouse;
SetPort(window);
mouse = event->where; // get the click position
GlobalToLocal(&mouse);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// space bar is used to deactiate the alarm
void AlarmWindowKeyDown(AlarmWindowPtr alarm, unsigned char key, short modifiers)
{
#pragma unused (alarm, modifiers)
switch (key) {
case charSpace:
StopPlaying();
DeactivateAlarm();
break;
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// create the configuration window
void ConfigureAlarm(void)
{
ConfigurationWindowPtr configure;
ControlHandle control;
RectPtr *rectHandle;
short i;
// first look if this window is alreay open, and if so just select it
configure = FindMyWindow(kConfigurationWindow);
if (configure != nil) {
SelectWindow((WindowPtr)configure);
return;
}
configure = GetMyWindow(sizeof(ConfigurationWindow), kConfigurationWindow);
FailIf(configure == nil, NoWindow)
configure->message = GetString(kConfigureMessage);
FailIf(configure->message == nil, NoString);
configure->sensitivityTitle = GetString(kSensitivityTitle);
FailIf(configure->sensitivityTitle == nil, NoString);
configure->max = GetString(kSensitivityMax);
FailIf(configure->max == nil, NoString);
configure->min = GetString(kSensitivityMin);
FailIf(configure->min == nil, NoString);
rectHandle = (RectPtr *)GetResource('RECT', kConfigureLevelRect);
FailIf(rectHandle == nil, NoRect);
configure->levelRect = **rectHandle;
rectHandle = (RectPtr *)GetResource('RECT', kTestLightRect);
FailIf(rectHandle == nil, NoRect);
configure->testLightRect = **rectHandle;
rectHandle = (RectPtr *)GetResource('RECT', kSensitivityRect);
FailIf(rectHandle == nil, NoRect);
configure->sensitivityRect = **rectHandle;
rectHandle = (RectPtr *)GetResource('RECT', kConfigureMsgRect);
FailIf(rectHandle == nil, NoRect);
configure->msgRect = **rectHandle;
control = GetMyControl(kConfigurationCancel, (WindowPtr)configure);
FailIf(control == nil, NoControl);
control = GetMyControl(kConfigurationOK, (WindowPtr)configure);
FailIf(control == nil, NoControl);
// need to initialize the levels for averaging
for (i = kNumberOfLevels - 1; i >= 0; --i)
UpdateAverageLevel();
configure->testLightOn = false;
configure->sensitivity = GetSensitivity();
SetPort((WindowPtr)configure);
TextSize(kSensitivityFontSize);
TextFont(applFont);
ShowWindow((WindowPtr)configure);
gAlarmState = kConfiguringState;
AdjustMenus(true);
return;
NoControl:
NoRect:
NoString:
CloseMyWindow(configure);
NoWindow:
AlertUser(MemError(), eNoWindow, kFatalError);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void DrawConfigureWindow(ConfigurationWindowPtr configure)
{
Rect frame;
PenNormal();
frame = configure->levelRect;
FrameRect(&frame);
UpdateLevels(configure);
frame = configure->testLightRect;
FrameRect(&frame);
DrawSensitivityCntl(configure);
UpdateTestLight(configure, configure->testLightOn);
HLock((Handle)configure->message);
TextBox(*configure->message + 1, **configure->message, &configure->msgRect, teJustLeft);
HUnlock((Handle)configure->message);
UpdateControls((WindowPtr)configure, ((WindowPtr)configure)->visRgn);
DeviceLoop(((WindowPtr)configure)->visRgn, (DeviceLoopDrawingProcPtr)OutlineControl,
(long)(FindMyControl((WindowPtr)configure, kConfigurationOK)), 0);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// draw the percentage bar, 0% at the bottom and 100% at the top
void DrawSensitivityCntl(ConfigurationWindowPtr configure)
{
FontInfo info;
RGBColor backColor;
RGBColor foreColor;
RGBColor grayColor;
Rect sensitivityRect;
Rect globalRect;
short sensitivityWidth;
short titleWidth;
short newTop;
Boolean isColor;
sensitivityRect = configure->sensitivityRect;
sensitivityWidth = sensitivityRect.right - sensitivityRect.left;
HLock((Handle)configure->sensitivityTitle);
titleWidth = StringWidth(*configure->sensitivityTitle);
// move to above the control a little, and center it according to the widths
MoveTo((sensitivityRect.left + (sensitivityWidth >> 1)) - (titleWidth >> 1),
sensitivityRect.top - kSensitivitySpace);
DrawString(*configure->sensitivityTitle);
HUnlock((Handle)configure->sensitivityTitle);
GetFontInfo(&info);
// move to the top of the control, but draw the text under the top
MoveTo(sensitivityRect.right + kSensitivitySpace,
sensitivityRect.top + info.ascent + info.descent + info.leading);
HLock((Handle)configure->max);
DrawString(*configure->max);
HUnlock((Handle)configure->max);
// move to the bottom of the control
MoveTo(sensitivityRect.right + kSensitivitySpace, sensitivityRect.bottom);
HLock((Handle)configure->min);
DrawString(*configure->min);
HUnlock((Handle)configure->min);
FrameRect(&sensitivityRect);
InsetRect(&sensitivityRect, 1, 1);
newTop = sensitivityRect.bottom - sensitivityRect.top;
newTop = (newTop * (100 - configure->sensitivity)) / 100;
sensitivityRect.top = sensitivityRect.bottom - newTop;
// read the warning about using patterns in the MPW E.T.O #6 CD ROM
// they were defined as an array, but should have been a struct
// the standard Think C includes is out of date, but I'm using the ETO #6 release
FillRect(&sensitivityRect, &qd.black);
sensitivityRect.bottom = sensitivityRect.top;
sensitivityRect.top = configure->sensitivityRect.top + 1;
// check for a color window
if (((CGrafPtr)configure)->portVersion & kIsColorPort) {
isColor = true;
GetBackColor(&backColor);
GetForeColor(&foreColor);
grayColor = foreColor;
globalRect = sensitivityRect;
LocalToGlobal(&TopLeft(globalRect));
LocalToGlobal(&BottomRight(globalRect));
}
else
isColor = false;
// find a nice gray for color windows, otherwise use a gray pattern
if ((isColor) && GetGray(GetRectDevice(globalRect), &backColor, &grayColor)) {
RGBForeColor(&grayColor);
PaintRect(&sensitivityRect);
RGBForeColor(&foreColor);
}
else
FillRect(&sensitivityRect, &qd.ltGray);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// the user is dragging the sensitivity level control, so track this until their done
void TrackSensitivityCntl(ConfigurationWindowPtr configure, Point mouse)
{
short sensitivity;
Point newPosition;
newPosition = mouse;
while (StillDown() && PtInRect(newPosition, &configure->sensitivityRect)) {
sensitivity = (newPosition.v - configure->sensitivityRect.top) * 100;
sensitivity /= configure->sensitivityRect.bottom - configure->sensitivityRect.top;
configure->sensitivity = sensitivity;
SetSensitivity(sensitivity);
DrawSensitivityCntl(configure);
GetMouse(&newPosition);
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// this light will flash when the alarm would have been turned on, and there should
// be a short amount of delay to let the user see it
void UpdateTestLight(ConfigurationWindowPtr configure, Boolean on)
{
Rect frame;
frame = configure->testLightRect;
InsetRect(&frame, 1, 1);
configure->testLightOn = on;
ForeColor(redColor);
if (on)
configure->lastTickCount = TickCount();
else if (configure->lastTickCount + kVisualDelay < TickCount()) {
ForeColor(whiteColor);
configure->testLightOn = !on;
}
PaintRect(&frame);
ForeColor(blackColor);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// draw the current volume levels to show the user volume feedback
void UpdateLevels(ConfigurationWindowPtr configure)
{
Rect levelRect;
short curLevel;
short averageLevel;
short newTop;
SetPort((WindowPtr)&configure->window);
PenNormal();
curLevel = GetCurLevel();
averageLevel = GetAverageLevel();
levelRect = configure->levelRect;
InsetRect(&levelRect, 1, 1);
// divide by 225 (max level) quickly by shifting to the right
newTop = levelRect.bottom - levelRect.top;
newTop = (newTop * averageLevel) >> 8;
levelRect.top = levelRect.bottom - newTop;
ForeColor(greenColor);
PaintRect(&levelRect);
if (curLevel > averageLevel) {
levelRect.bottom = levelRect.top;
newTop = configure->levelRect.bottom - configure->levelRect.top;
newTop = (newTop * curLevel) >> 8;
levelRect.top = configure->levelRect.bottom - newTop;
ForeColor(yellowColor);
PaintRect(&levelRect);
}
ForeColor(blackColor);
levelRect.bottom = levelRect.top;
levelRect.top = configure->levelRect.top;
EraseRect(&levelRect);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void ConfiguringWindowClick(ConfigurationWindowPtr configure, EventRecord *event)
{
Point mouse;
ControlHandle control;
mouse = event->where;
GlobalToLocal(&mouse);
if (FindControl(mouse, (WindowPtr)configure, &control)) {
if (TrackControl(control, mouse, nil)) {
switch (GetCRefCon(control)) {
case kConfigurationCancel:
gAlarmState = kDeactivatedState;
CloseMyWindow(configure);
break;
case kConfigurationOK:
SetSensitivity(configure->sensitivity);
CloseMyWindow(configure);
ActivateAlarm();
break;
}
}
}
else if ( PtInRect(mouse, &configure->sensitivityRect) )
TrackSensitivityCntl(configure, mouse);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void ConfigurationKeyDown(ConfigurationWindowPtr configure, unsigned char key, short modifiers)
{
switch (key) {
case charEnter:
case charReturn:
SelectButton(FindMyControl((WindowPtr)configure, kConfigurationOK));
SetSensitivity(configure->sensitivity);
CloseMyWindow(configure);
ActivateAlarm();
break;
case charEscape:
SelectButton(FindMyControl((WindowPtr)configure, kConfigurationCancel));
gAlarmState = kDeactivatedState;
CloseMyWindow(configure);
break;
case charPeriod:
if (modifiers & cmdKey) {
SelectButton(FindMyControl((WindowPtr)configure, kConfigurationCancel));
gAlarmState = kDeactivatedState;
CloseMyWindow(configure);
}
break;
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void ActivateAlarm(void)
{
ActivationWindowPtr activation;
ControlHandle control;
RectPtr *rectHandle;
activation = GetMyWindow(sizeof(ActivationWindow), kActivationWindow);
FailIf(activation == nil, NoWindow)
control = GetMyControl(kCancelCountDown, (WindowPtr)activation);
FailIf(control == nil, NoControl);
rectHandle = (RectPtr *)GetResource('RECT', kActivationCountRect);
FailIf(rectHandle == nil, NoRect);
activation->delayPt.h = (**rectHandle).right;
activation->delayPt.v = (**rectHandle).bottom;
rectHandle = (RectPtr *)GetResource('RECT', kActivationMsgRect);
FailIf(rectHandle == nil, NoRect);
activation->msgRect = **rectHandle;
activation->message = GetString(kActivationMessage);
FailIf(activation->message == nil, NoString);
activation->lastTickCount = TickCount();
activation->secondsCountDown = kActivationDelay;
ShowWindow((WindowPtr)activation);
gAlarmState = kActivatingState;
AdjustMenus(true);
return;
NoString:
NoRect:
NoControl:
CloseMyWindow(activation);
NoWindow:
AlertUser(MemError(), eNoWindow, kFatalError);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// attempt to deactivate the alarm based on the current state
void DeactivateAlarm(void)
{
AlarmWindowPtr alarm;
ActivationWindowPtr activation;
DeactivationWindowPtr deactivation;
OSErr err;
switch (gAlarmState) {
case kActivatingState:
gAlarmState = kDeactivatedState;
activation = FindMyWindow(kActivationWindow);
FailIf(activation == nil, Failure);
SelectButton(FindMyControl((WindowPtr)activation, kCancelCountDown));
activation->secondsCountDown = 0;
CloseMyWindow(activation);
break;
case kActivatedState:
gAlarmState = kDeactivatingState;
GetPassword();
AdjustMenus(true);
break;
case kDeactivatingState:
gAlarmState = kDeactivatedState;
err = PlaySound(kAlarmDeactivatedSnd);
FailMsg(err != noErr);
deactivation = FindMyWindow(kDeactivationWindow);
FailIf(deactivation == nil, Failure);
CloseMyWindow(deactivation);
alarm = FindMyWindow(kAlarmWindow);
FailIf(alarm == nil, Failure);
CloseMyWindow(alarm);
AdjustMenus(true);
break;
default:
FailIf("Bad State", Failure);
break;
}
Failure:
return;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void DrawActivationWindow(ActivationWindowPtr activation)
{
TextFont(systemFont);
TextSize(kSystemFontSize);
HLock((Handle)activation->message);
TextBox(*activation->message + 1, **activation->message, &activation->msgRect, teJustLeft);
HUnlock((Handle)activation->message);
DrawCountDown((WindowPtr)&activation->window, activation->delayPt,
&activation->lastTickCount, &activation->secondsCountDown);
UpdateControls((WindowPtr)activation, ((WindowPtr)activation)->visRgn);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void ActivatingWindowClick(WindowPtr window, EventRecord *event)
{
Point mouse;
ControlHandle control;
mouse = event->where; // get the click position
GlobalToLocal(&mouse);
if (FindControl(mouse, window, &control)) {
if (TrackControl(control, mouse, nil)) {
switch (GetCRefCon(control)) {
case kCancelCountDown:
DeactivateAlarm();
break;
}
}
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void ActivationWindowKeyDown(ActivationWindowPtr activation, unsigned char key, short modifiers)
{
#pragma unused (activation)
switch (key) {
case charEscape:
DeactivateAlarm();
break;
case charPeriod:
if (modifiers & cmdKey)
DeactivateAlarm();
break;
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void SetPassword(void)
{
SetPasswordWindowPtr setPassword;
ControlHandle control;
RectPtr *rectHandle;
setPassword = GetMyWindow(sizeof(SetPasswordWindow), kSetPasswordWindow);
FailIf(setPassword == nil, NoWindow)
control = GetMyControl(kSetPasswordCancel, (WindowPtr)setPassword);
FailIf(control == nil, NoControl);
control = GetMyControl(kSetPasswordOK, (WindowPtr)setPassword);
FailIf(control == nil, NoControl);
rectHandle = (RectPtr *)GetResource('RECT', kSetPasswordMsgRect);
FailIf(rectHandle == nil, NoRect);
setPassword->msgRect = **rectHandle;
rectHandle = (RectPtr *)GetResource('RECT', kSetPasswordRect);
FailIf(rectHandle == nil, NoRect);
setPassword->passwordRect = **rectHandle;
setPassword->message = GetString(kSetPassword1Message);
FailIf(setPassword->message == nil, NoString);
setPassword->firstPhase = true;
setPassword->accepted = false;
*setPassword->password1 = 0; // zero length string, so far
*setPassword->password2 = 0;
SetPort((WindowPtr)setPassword);
TextFont(systemFont);
TextSize(kSystemFontSize);
ShowWindow((WindowPtr)setPassword);
AdjustMenus(true);
return;
NoString:
NoRect:
NoControl:
CloseMyWindow(setPassword);
NoWindow:
AlertUser(MemError(), eNoWindow, kFatalError);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void DrawSetPassword(SetPasswordWindowPtr setPassword)
{
Rect frame;
unsigned char length;
frame = setPassword->passwordRect;
FrameRect(&frame);
HLock((Handle)setPassword->message);
TextBox(*setPassword->message + 1, **setPassword->message, &setPassword->msgRect, teJustLeft);
HUnlock((Handle)setPassword->message);
if (setPassword->firstPhase)
length = Length(setPassword->password1);
else
length = Length(setPassword->password2);
DrawPassword(setPassword->passwordRect, length);
UpdateControls((WindowPtr)setPassword, ((WindowPtr)&setPassword->window)->visRgn);
DeviceLoop(((WindowPtr)setPassword)->visRgn, (DeviceLoopDrawingProcPtr)OutlineControl,
(long)(FindMyControl((WindowPtr)setPassword, kSetPasswordOK)), 0);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// the OK button was selected, make sure the user entered the password the same
// way twice.
void DoPasswordOKButton(SetPasswordWindowPtr setPassword)
{
Handle newPassword;
Rect drawArea;
if (setPassword->firstPhase) {
// ask the user to enter the password a second time
setPassword->firstPhase = false;
setPassword->message = GetString(kSetPassword2Message);
FailIf(setPassword->message == nil, Failure);
InvalRect(&setPassword->msgRect);
drawArea = setPassword->passwordRect;
InsetRect(&drawArea, kEditTextInset, kEditTextInset);
EraseRect(&drawArea);
InvalRect(&drawArea);
}
else {
// compare the second password, accept it or startover
if (IUEqualString(setPassword->password1, setPassword->password2) == 0)
setPassword->accepted = true;
else {
// start over, and do it all over again from the beginning
DoActivate((WindowPtr)setPassword, false);
Alert(rPasswordsDoNotMatch, nil);
*setPassword->password1 = 0; // zero length string, so far
*setPassword->password2 = 0;
setPassword->firstPhase = true;
setPassword->message = GetString(kSetPassword1Message);
FailIf(setPassword->message == nil, Failure);
InvalRect(&setPassword->msgRect);
drawArea = setPassword->passwordRect;
InsetRect(&drawArea, kEditTextInset, kEditTextInset);
EraseRect(&drawArea);
InvalRect(&drawArea);
}
}
if (setPassword->accepted) {
// save the user's new password, and close the dialog window
newPassword = GetResource(kPasswordResType, kPasswordResID);
FailIf(newPassword == nil, Failure);
SetHandleSize(newPassword, kLengthOfPassword);
PascalStringCopy(setPassword->password1, (StringPtr)*newPassword);
ChangedResource(newPassword);
WriteResource(newPassword);
CloseMyWindow(setPassword);
}
Failure:
return;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void SetPasswordKeyDown(SetPasswordWindowPtr setPassword, unsigned char key, short modifiers)
{
StringPtr password;
unsigned char length;
if (setPassword->firstPhase)
password = setPassword->password1;
else
password = setPassword->password2;
switch (key) {
case charBackspace:
*password = 0;
DrawPassword(setPassword->passwordRect, 0);
break;
case charEnter:
case charReturn:
SelectButton(FindMyControl((WindowPtr)setPassword, kSetPasswordOK));
DoPasswordOKButton(setPassword);
break;
case charEscape:
SelectButton(FindMyControl((WindowPtr)setPassword, kSetPasswordCancel));
CloseMyWindow(setPassword);
break;
case charPeriod:
if (modifiers & cmdKey) {
SelectButton(FindMyControl((WindowPtr)setPassword, kSetPasswordCancel));
CloseMyWindow(setPassword);
}
break;
default:
length = LimitStringLength(password, kLengthOfPassword);
password[length] = key;
SetPort((WindowPtr)setPassword);
DrawPassword(setPassword->passwordRect, length);
break;
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void SetPasswordClick(SetPasswordWindowPtr setPassword, EventRecord *event)
{
Point mouse;
ControlHandle control;
mouse = event->where; // get the click position
GlobalToLocal(&mouse);
if (FindControl(mouse, (WindowPtr)setPassword, &control)) {
if (TrackControl(control, mouse, nil)) {
switch (GetCRefCon(control)) {
case kSetPasswordCancel:
CloseMyWindow(setPassword);
break;
case kSetPasswordOK:
DoPasswordOKButton(setPassword);
break;
}
}
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void GetPassword(void)
{
DeactivationWindowPtr deactivation;
ControlHandle control;
RectPtr *rectHandle;
deactivation = GetMyWindow(sizeof(DeactivationWindow), kDeactivationWindow);
FailIf(deactivation == nil, NoWindow)
control = GetMyControl(kDeactivationOK, (WindowPtr)deactivation);
FailIf(control == nil, NoControl);
rectHandle = (RectPtr *)GetResource('RECT', kPasswordRect);
FailIf(rectHandle == nil, NoRect);
deactivation->passwordRect = **rectHandle;
rectHandle = (RectPtr *)GetResource('RECT', kDeactivationMsgRect);
FailIf(rectHandle == nil, NoRect);
deactivation->msgRect = **rectHandle;
rectHandle = (RectPtr *)GetResource('RECT', kDeactivationCountRect);
FailIf(rectHandle == nil, NoRect);
deactivation->delayPt.h = (**rectHandle).right;
deactivation->delayPt.v = (**rectHandle).bottom;
deactivation->message = GetString(kDeactivationMessage);
FailIf(deactivation->message == nil, NoString);
deactivation->lastTickCount = TickCount();
deactivation->secondsCountDown = kDeactivationDelay;
ShowWindow((WindowPtr)deactivation);
AdjustMenus(true);
return;
NoString:
NoRect:
NoControl:
CloseMyWindow(deactivation);
NoWindow:
AlertUser(MemError(), eNoWindow, kFatalError);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void DrawDeactivationWindow(DeactivationWindowPtr deactivation)
{
TextFont(systemFont);
TextSize(kSystemFontSize);
HLock((Handle)deactivation->message);
TextBox(*deactivation->message + 1, **deactivation->message,
&deactivation->msgRect, teJustLeft);
HUnlock((Handle)deactivation->message);
FrameRect(&deactivation->passwordRect);
DrawPassword(deactivation->passwordRect, Length(deactivation->password));
DrawCountDown((WindowPtr)&deactivation->window, deactivation->delayPt,
&deactivation->lastTickCount, &deactivation->secondsCountDown);
UpdateControls((WindowPtr)deactivation, ((WindowPtr)deactivation)->visRgn);
DeviceLoop(((WindowPtr)deactivation)->visRgn, (DeviceLoopDrawingProcPtr)OutlineControl,
(long)(FindMyControl((WindowPtr)deactivation, kDeactivationOK)), 0);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void DeactivationKeyDown(DeactivationWindowPtr deactivation, unsigned char key, short modifiers)
{
#pragma unused (modifiers)
unsigned char length;
switch (key) {
case charBackspace:
*deactivation->password = 0;
SetPort((WindowPtr)deactivation);
DrawPassword(deactivation->passwordRect, 0);
break;
case charEnter:
case charReturn:
SelectButton(FindMyControl((WindowPtr)deactivation, kDeactivationOK));
DeactivationOKButton(deactivation);
break;
default:
length = LimitStringLength(deactivation->password, kLengthOfPassword);
deactivation->password[length] = key;
SetPort((WindowPtr)deactivation);
DrawPassword(deactivation->passwordRect, length);
break;
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Boolean IsPasswordValid(DeactivationWindowPtr deactivation)
{
StringHandle newPassword;
newPassword = (StringHandle)GetResource(kPasswordResType, kPasswordResID);
FailIf(newPassword == nil, Failure);
return (IUEqualString(deactivation->password, *newPassword) == 0);
Failure:
return (false);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void DeactivationOKButton(DeactivationWindowPtr deactivation)
{
OSErr err;
if (IsPasswordValid(deactivation))
DeactivateAlarm();
else {
err = PlaySound(kAlarmTriggeredSnd);
FailIf(err != noErr, Failure);
*deactivation->password = 0;
DrawPassword(deactivation->passwordRect, 0);
}
Failure:
return;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void DeactivationWindowClick(DeactivationWindowPtr deactivation, EventRecord *event)
{
Point mouse;
ControlHandle control;
mouse = event->where; // get the click position
GlobalToLocal(&mouse);
if (FindControl(mouse, (WindowPtr)deactivation, &control)) {
if (TrackControl(control, mouse, nil)) {
switch (GetCRefCon(control)) {
case kDeactivationOK:
DeactivationOKButton(deactivation);
break;
}
}
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Calculate a sleep value for WaitNextEvent. This takes into account the things
// that DoIdle does with idle time which happens if the alarm is activated.
long GetSleep(void)
{
if (gAlarmState > kDeactivatedState)
return (kAlarmedSleep);
else
return (kNotAlarmedSleep);
}